package com.project.biz.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.project.biz.*;
import com.project.common.enums.LockEnum;
import com.project.common.exception.ServiceException;
import com.project.entity.CmsOrder;
import com.project.entity.CmsRecoveryMember;
import com.project.entity.CmsRecoveryMemberRecharge;
import com.project.entity.CmsUser;
import com.project.mapper.CmsRecoveryMemberRechargeMapper;
import com.project.pojo.dto.*;
import com.project.pojo.model.RecoveryMemberRechargeModel;
import com.project.redis.RedisClient2;
import com.project.util.wx.WxPaySignUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * @Description: 回收员充值业务层接口实现类
 * @Author: zhuchi
 * @CreateDate: 2020/12/5 21:58
 * @Version: 1.0
 */
@Slf4j
@Service
public class CmsRecoveryMemberRechargeBizImpl extends ServiceImpl<CmsRecoveryMemberRechargeMapper, CmsRecoveryMemberRecharge> implements CmsRecoveryMemberRechargeBiz {

    /**
     * 用户业务层接口类
     */
    @Autowired
    private UserBiz userBiz;

    /**
     * 用户余额接口类
     */
    @Autowired
    private CmsUserBalanceBiz userBalanceBiz;

    /**
     * 回收员业务层接口类
     */
    @Autowired
    private CmsRecoveryMemberBiz cmsRecoveryMemberBiz;

    /**
     * Redis服务
     */
    @Autowired
    private RedisClient2 redisClient2;

    /**
     * 微信支付通知信息业务层接口类
     */
    @Autowired
    private CmsWxPayNoticeBiz wxPayNoticeBiz;

    /**
     * 资金流水业务层接口类
     */
    @Autowired
    private CmsCapitalFlowBiz capitalFlowBiz;

    /**
     * 回收员充值
     * @param recoveryMemberRechargeModel
     * @param userId
     * @return
     */
    @Transactional(rollbackFor = Exception.class , isolation = Isolation.REPEATABLE_READ, timeout = 30)
    public RecoveryMemberRechargeDTO addRecoveryMemberRecharge(RecoveryMemberRechargeModel recoveryMemberRechargeModel, Long userId){
        CmsUser user = this.userBiz.getById(userId);
        //用户类型（0:普通用户,1:回收员）
        if (user.getUserType() != 1){
            throw new ServiceException("请先成为回收员后再充值");
        }
        //获取回收员信息
        CmsRecoveryMember recoveryMember = this.cmsRecoveryMemberBiz.getRecoveryMmberByUserId(userId);
        if (null == recoveryMember){
            throw new ServiceException("请先成为回收员后再充值");
        }

        RecoveryMemberRechargeDTO recoveryMemberRechargeDTO = new RecoveryMemberRechargeDTO();

        //回收员充值分布式锁
        String addRecoveryMemberRecharge_lock = LockEnum.ADD_RECOVERY_MEMBER_RECHARGE.Value() + userId;
        //超时时间（15秒）
        Long expires = System.currentTimeMillis() + Long.parseLong(LockEnum.LOCK_EXPIRES.Value());
        //用户加锁
        if (this.redisClient2.lock(addRecoveryMemberRecharge_lock, String.valueOf(expires))){
            try {
                //当前时间
                Date nowTime = new Date();
                //订单号
                String orderNumber = RandomStringUtils.randomAlphanumeric(20);

                String description = "充值";

                Calendar calendar = Calendar.getInstance();
                calendar.setTime(nowTime);
                //当前时间加1小时
                calendar.add(Calendar.HOUR_OF_DAY, 1);
                //微秒设置为0,防止mysql自动加一秒
                //MySQL数据库对于毫秒大于500的数据进行进位
                calendar.set(Calendar.MILLISECOND, 0);

                //最晚支付时间
                Date deadlineTime = calendar.getTime();

                //充值转换为分
                int h5PayAmount = recoveryMemberRechargeModel.getTotalAmount().multiply(new BigDecimal("100.00")).setScale(2, BigDecimal.ROUND_HALF_UP).intValue();

                //组装调用H5支付下单参数,调用下单接口
                WxPayH5CreateOrderDTO wxPayH5CreateOrderDTO = new WxPayH5CreateOrderDTO();
                wxPayH5CreateOrderDTO.setOutTradeNo(orderNumber);
                wxPayH5CreateOrderDTO.setDescription(description);
                wxPayH5CreateOrderDTO.setTimeExpire(deadlineTime);
                wxPayH5CreateOrderDTO.setTotal(h5PayAmount);
                wxPayH5CreateOrderDTO.setPayerClientIp(recoveryMemberRechargeModel.getIp());
                wxPayH5CreateOrderDTO.setPaySysType(2);//类型（1:商品支付,2:回收员充值）
                //调用微信H5下单接口
                WxPayH5CreateOrderResultDTO wxPayH5CreateOrderResultDTO = WxPaySignUtil.wxPayH5CreateOrder(wxPayH5CreateOrderDTO);
                if (wxPayH5CreateOrderResultDTO.getCode().equals("200")){
                    CmsRecoveryMemberRecharge recoveryMemberRecharge = new CmsRecoveryMemberRecharge();
                    recoveryMemberRecharge.setRecoveryMemberId(recoveryMember.getRecoveryMemberId());
                    recoveryMemberRecharge.setUserId(userId);
                    recoveryMemberRecharge.setOrderNumber(orderNumber);
                    recoveryMemberRecharge.setTotalAmount(recoveryMemberRechargeModel.getTotalAmount());
                    recoveryMemberRecharge.setGiveAmount(new BigDecimal("0.00"));
                    recoveryMemberRecharge.setH5Url(wxPayH5CreateOrderResultDTO.getH5Url());
                    recoveryMemberRecharge.setCreateTime(nowTime);
                    recoveryMemberRecharge.setState(0);
                    recoveryMemberRecharge.setStatus(1);

                    //添加充值记录
                    int addState = this.baseMapper.insert(recoveryMemberRecharge);

                    if (addState > 0){
                        BeanUtils.copyProperties(recoveryMemberRecharge, recoveryMemberRechargeDTO);
                        try {
                            recoveryMemberRechargeDTO.setH5Url(URLEncoder.encode(wxPayH5CreateOrderResultDTO.getH5Url(), "UTF-8"));
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                }else {
                    throw new ServiceException("充值失败");
                }
            }catch (Exception e){
                e.printStackTrace();
                log.info("垃圾回收下单时发生异常:{}", userId, e.getMessage());
                throw new ServiceException(e.getMessage());
            }finally {
                //解锁
                this.redisClient2.unlock(addRecoveryMemberRecharge_lock, String.valueOf(expires));
            }
        }else {
            //操作太频繁了，请稍后再试
            throw new ServiceException("操作太频繁了，请稍后再试");
        }

        return recoveryMemberRechargeDTO;
    }

    /**
     * 回收员充值成功
     * @param jsonObject
     * @return 0:失败,1:成功,2:已处理过,3:订单不存在
     */
    @Transactional(rollbackFor = Exception.class , isolation = Isolation.REPEATABLE_READ, timeout = 30)
    public int updateRecoveryMemberRechargeNotify(JSONObject jsonObject){
        int result = 0;
        //商户订单号
        String out_trade_no = jsonObject.getString("out_trade_no");

        if (StringUtils.isNotBlank(out_trade_no)){
            //根据订单号获取订单信息
            CmsRecoveryMemberRecharge recoveryMemberRecharge = this.getRechargeByOrderNumber(out_trade_no);
            if (null != recoveryMemberRecharge){
                //状态（0:待付款、1:已支付）
                if (recoveryMemberRecharge.getState() == 0){
                    //回收员充值成功更新订单状态分布式锁
                    String updateRecoveryMemberRechargeNotify_lock = LockEnum.UPDATE_RECOVERY_MEMBER_RECHARGE_NOTIFY.Value() + out_trade_no;
                    //超时时间（15秒）
                    Long expires = System.currentTimeMillis() + Long.parseLong(LockEnum.LOCK_EXPIRES.Value());
                    //锁定订单
                    boolean orderLock = this.redisClient2.lock(updateRecoveryMemberRechargeNotify_lock, String.valueOf(expires));
                    if (orderLock){
                        //微信支付订单号
                        String transaction_id = jsonObject.getString("transaction_id");

                        //支付完成时间
                        String success_time = jsonObject.getString("success_time");
                        success_time = success_time.replace("T", " ");
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                        //用户支付金额，单位为分
                        Integer payer_total = jsonObject.getJSONObject("amount").getInteger("payer_total");
                        //用户支付金额,转为元
                        BigDecimal payAmount = new BigDecimal(String.valueOf(payer_total)).divide(new BigDecimal("100.00"), 2, BigDecimal.ROUND_HALF_UP);
                        try {
                            Date payTime = dateFormat.parse(success_time);

                            recoveryMemberRecharge.setPayAmount(payAmount);
                            recoveryMemberRecharge.setPayTime(payTime);
                            recoveryMemberRecharge.setTradeNo(transaction_id);
                            recoveryMemberRecharge.setState(1);//状态（0:待付款、1:已支付）

                            int updateOrderState = this.baseMapper.updateById(recoveryMemberRecharge);
                            log.info("回收员充值成功,更新订单状态执行结果:{}", updateOrderState);
                            if (updateOrderState > 0){
                                result = 1;

                                //获取用户钱包金额
                                BigDecimal balance = this.userBalanceBiz.getUserBalance(recoveryMemberRecharge.getUserId());

                                //添加充值记录
                                AddRechargeFlowDTO addRechargeFlowDTO = new AddRechargeFlowDTO();
                                addRechargeFlowDTO.setRechargeType(1);//充值类型（1:个人充值,2:系统上分）
                                addRechargeFlowDTO.setPayType(2);//支付方式（0:银行卡,1:支付宝,2:微信）
                                addRechargeFlowDTO.setAmount(payAmount);
                                addRechargeFlowDTO.setIncomeName("充值");
                                addRechargeFlowDTO.setUserAmount(balance);
                                int addRechargeFlow = this.capitalFlowBiz.addRechargeFlow(addRechargeFlowDTO, recoveryMemberRecharge.getUserId());
                                log.info("回收员[{}]充值成功使用微信H5付款支付[{}]元,增加充值记录执行状态:{}", recoveryMemberRecharge.getUserId(), payAmount, addRechargeFlow);

                                //增加用户余额 = 充值金额 + 赠送金额
                                BigDecimal addAmount = recoveryMemberRecharge.getTotalAmount().add(recoveryMemberRecharge.getGiveAmount());
                                //将钱冲进用户余额
                                int addUserBalance = this.userBalanceBiz.updateAddUserBalance(recoveryMemberRecharge.getUserId(), addAmount);
                                log.info("回收员[{}]充值成功将钱[{}]冲进用户余额执行状态:{}", recoveryMemberRecharge.getUserId(), addAmount, addUserBalance);
                            }
                        }catch (Exception e){
                            System.out.println("时间转换异常");
                        }finally {
                            this.redisClient2.unlock(updateRecoveryMemberRechargeNotify_lock, String.valueOf(expires));
                        }
                        //保存微信支付异步通知信息
                        this.wxPayNoticeBiz.addWxPayNotice("MWEB", 2, jsonObject.toJSONString());
                    }
                }else {
                    result = 2;
                }
            }else {
                result = 3;
            }
        }else {
            result = 3;
        }

        return result;
    }

    /**
     * 根据订单号获取充值记录
     * @param orderNumber
     * @return
     */
    public CmsRecoveryMemberRecharge getRechargeByOrderNumber(String orderNumber){
        QueryWrapper<CmsRecoveryMemberRecharge> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_number", orderNumber);
        CmsRecoveryMemberRecharge recoveryMemberRecharge = this.baseMapper.selectOne(queryWrapper);

        return recoveryMemberRecharge;
    }

}
